import pandas as pd
import numpy as np
import math
import seaborn as sns
import plotly.express as px
import matplotlib.pyplot as plt
import plotly.io as pio
import plotly.graph_objs as go
from matplotlib import style
from plotly.offline import init_notebook_mode, iplot
from plotly.subplots import make_subplots
from scipy import stats as st
from scipy.stats import levene
from statsmodels.graphics.gofplots import qqplot
data_game = pd.read_csv('games.csv')
Проведем разведочный анализ данных
display(data_game.head(), data_game.tail())
Name | Platform | Year_of_Release | Genre | NA_sales | EU_sales | JP_sales | Other_sales | Critic_Score | User_Score | Rating | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | Wii Sports | Wii | 2006.0 | Sports | 41.36 | 28.96 | 3.77 | 8.45 | 76.0 | 8 | E |
1 | Super Mario Bros. | NES | 1985.0 | Platform | 29.08 | 3.58 | 6.81 | 0.77 | NaN | NaN | NaN |
2 | Mario Kart Wii | Wii | 2008.0 | Racing | 15.68 | 12.76 | 3.79 | 3.29 | 82.0 | 8.3 | E |
3 | Wii Sports Resort | Wii | 2009.0 | Sports | 15.61 | 10.93 | 3.28 | 2.95 | 80.0 | 8 | E |
4 | Pokemon Red/Pokemon Blue | GB | 1996.0 | Role-Playing | 11.27 | 8.89 | 10.22 | 1.00 | NaN | NaN | NaN |
Name | Platform | Year_of_Release | Genre | NA_sales | EU_sales | JP_sales | Other_sales | Critic_Score | User_Score | Rating | |
---|---|---|---|---|---|---|---|---|---|---|---|
16710 | Samurai Warriors: Sanada Maru | PS3 | 2016.0 | Action | 0.00 | 0.00 | 0.01 | 0.0 | NaN | NaN | NaN |
16711 | LMA Manager 2007 | X360 | 2006.0 | Sports | 0.00 | 0.01 | 0.00 | 0.0 | NaN | NaN | NaN |
16712 | Haitaka no Psychedelica | PSV | 2016.0 | Adventure | 0.00 | 0.00 | 0.01 | 0.0 | NaN | NaN | NaN |
16713 | Spirits & Spells | GBA | 2003.0 | Platform | 0.01 | 0.00 | 0.00 | 0.0 | NaN | NaN | NaN |
16714 | Winning Post 8 2016 | PSV | 2016.0 | Simulation | 0.00 | 0.00 | 0.01 | 0.0 | NaN | NaN | NaN |
data_game.shape
(16715, 11)
data_game.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 16715 entries, 0 to 16714 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Name 16713 non-null object 1 Platform 16715 non-null object 2 Year_of_Release 16446 non-null float64 3 Genre 16713 non-null object 4 NA_sales 16715 non-null float64 5 EU_sales 16715 non-null float64 6 JP_sales 16715 non-null float64 7 Other_sales 16715 non-null float64 8 Critic_Score 8137 non-null float64 9 User_Score 10014 non-null object 10 Rating 9949 non-null object dtypes: float64(6), object(5) memory usage: 1.4+ MB
display(data_game.describe(),
#описания категориальных признаков
data_game.describe(include=[object]))
Year_of_Release | NA_sales | EU_sales | JP_sales | Other_sales | Critic_Score | |
---|---|---|---|---|---|---|
count | 16446.000000 | 16715.000000 | 16715.000000 | 16715.000000 | 16715.000000 | 8137.000000 |
mean | 2006.484616 | 0.263377 | 0.145060 | 0.077617 | 0.047342 | 68.967679 |
std | 5.877050 | 0.813604 | 0.503339 | 0.308853 | 0.186731 | 13.938165 |
min | 1980.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 13.000000 |
25% | 2003.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 60.000000 |
50% | 2007.000000 | 0.080000 | 0.020000 | 0.000000 | 0.010000 | 71.000000 |
75% | 2010.000000 | 0.240000 | 0.110000 | 0.040000 | 0.030000 | 79.000000 |
max | 2016.000000 | 41.360000 | 28.960000 | 10.220000 | 10.570000 | 98.000000 |
Name | Platform | Genre | User_Score | Rating | |
---|---|---|---|---|---|
count | 16713 | 16715 | 16713 | 10014 | 9949 |
unique | 11559 | 31 | 12 | 96 | 8 |
top | Need for Speed: Most Wanted | PS2 | Action | tbd | E |
freq | 12 | 2161 | 3369 | 2424 | 3990 |
data_game.isna().sum()
Name 2 Platform 0 Year_of_Release 269 Genre 2 NA_sales 0 EU_sales 0 JP_sales 0 Other_sales 0 Critic_Score 8578 User_Score 6701 Rating 6766 dtype: int64
data_game.duplicated().sum()
0
Промежуточный вывод:
Year_of_Release
и Critic_Score
- на int, User_Score
- на floatdata_game = data_game.rename(columns={'Year_of_Release':'year', 'NA_sales':'na', \
'JP_sales':'jp', 'EU_sales':'eu', 'Other_sales':'other'})
data_game.columns = data_game.columns.str.lower()
Описание данных:
name
— название игры
platform
— платформа
year
— год выпуска
genre
— жанр игры
na
— продажи в Северной Америке (миллионы проданных копий)
eu
— продажи в Европе (миллионы проданных копий)
jp
— продажи в Японии (миллионы проданных копий)
other
— продажи в других странах (миллионы проданных копий)
critic_score
— оценка критиков (максимум 100)
user_score
— оценка пользователей (максимум 10)
rating
— рейтинг от организации ESRB (англ. Entertainment Software Rating Board). Эта ассоциация определяет рейтинг компьютерных игр и присваивает им подходящую возрастную категорию.
При проверке типы данных, была удивлена колонке user_score
- его формат 'object,
что очень странно, ведь если бы там были только числа и NaN'ы, то формат был бы 'float'
data_game.user_score.unique()
array(['8', nan, '8.3', '8.5', '6.6', '8.4', '8.6', '7.7', '6.3', '7.4', '8.2', '9', '7.9', '8.1', '8.7', '7.1', '3.4', '5.3', '4.8', '3.2', '8.9', '6.4', '7.8', '7.5', '2.6', '7.2', '9.2', '7', '7.3', '4.3', '7.6', '5.7', '5', '9.1', '6.5', 'tbd', '8.8', '6.9', '9.4', '6.8', '6.1', '6.7', '5.4', '4', '4.9', '4.5', '9.3', '6.2', '4.2', '6', '3.7', '4.1', '5.8', '5.6', '5.5', '4.4', '4.6', '5.9', '3.9', '3.1', '2.9', '5.2', '3.3', '4.7', '5.1', '3.5', '2.5', '1.9', '3', '2.7', '2.2', '2', '9.5', '2.1', '3.6', '2.8', '1.8', '3.8', '0', '1.6', '9.6', '2.4', '1.7', '1.1', '0.3', '1.5', '0.7', '1.2', '2.3', '0.5', '1.3', '0.2', '0.6', '1.4', '0.9', '1', '9.7'], dtype=object)
# Загуглив значение tbd - было выясненно - это to be determined, что в переводе "предстоит определить"
data_game.query('user_score == "tbd"').shape
(2424, 11)
Комментарий:
Данных с соответствующим значением, как мы видим большое количество.
Предлагаю заменить их пустым значением типа NAN для проведения дальнейшего анализа данных
data_game.user_score = data_game.user_score.replace('tbd', np.nan).astype('float')
data_game.query('user_score == "tbd"').shape
(0, 11)
data_game.year = data_game.year.astype('Int64')
data_game.critic_score = data_game.critic_score.astype('Int64')
Обработка пропущенных значений
# Начнем по-порядку. В колонке [name] всего два пропуска, проверим...
data_game.query('name != name')
name | platform | year | genre | na | eu | jp | other | critic_score | user_score | rating | |
---|---|---|---|---|---|---|---|---|---|---|---|
659 | NaN | GEN | 1993 | NaN | 1.78 | 0.53 | 0.00 | 0.08 | <NA> | NaN | NaN |
14244 | NaN | GEN | 1993 | NaN | 0.00 | 0.00 | 0.03 | 0.00 | <NA> | NaN | NaN |
Что касается колонки year
, пропусков - 269, пропуски невосполнить средним либо медианным значением...
Предлагаю провести маленький экспиремент, отобрать три игры и посмотреть год релиза для разных платформ
и на основе результатов по аналогии восполнить все пропущенные значения указанным годом на одной из платформ
data_game.query('year.isnull()')['name'].reset_index().head(3)
index | name | |
---|---|---|
0 | 183 | Madden NFL 2004 |
1 | 377 | FIFA Soccer 2004 |
2 | 456 | LEGO Batman: The Videogame |
# Красным подмечено пустые значения
display(data_game.query('name == "Madden NFL 2004"').style.highlight_null('red'),
data_game.query('name == "FIFA Soccer 2004"').style.highlight_null('red'),
data_game.query('name == "LEGO Batman: The Videogame"').style.highlight_null('red'))
name | platform | year | genre | na | eu | jp | other | critic_score | user_score | rating | |
---|---|---|---|---|---|---|---|---|---|---|---|
183 | Madden NFL 2004 | PS2 | Sports | 4.260000 | 0.260000 | 0.010000 | 0.710000 | 94 | 8.500000 | E | |
1881 | Madden NFL 2004 | XB | 2003 | Sports | 1.020000 | 0.020000 | 0.000000 | 0.050000 | 92 | 8.300000 | E |
3889 | Madden NFL 2004 | GC | 2003 | Sports | 0.400000 | 0.100000 | 0.000000 | 0.010000 | 94 | 7.700000 | E |
5708 | Madden NFL 2004 | GBA | 2003 | Sports | 0.220000 | 0.080000 | 0.000000 | 0.010000 | 70 | 6.600000 | E |
name | platform | year | genre | na | eu | jp | other | critic_score | user_score | rating | |
---|---|---|---|---|---|---|---|---|---|---|---|
377 | FIFA Soccer 2004 | PS2 | Sports | 0.590000 | 2.360000 | 0.040000 | 0.510000 | 84 | 6.400000 | E | |
2606 | FIFA Soccer 2004 | XB | 2003 | Sports | 0.240000 | 0.490000 | 0.000000 | 0.050000 | 82 | 8.200000 | E |
12029 | FIFA Soccer 2004 | GC | 2003 | Sports | 0.050000 | 0.010000 | 0.000000 | 0.000000 | 83 | 6.200000 | E |
13086 | FIFA Soccer 2004 | GBA | 2003 | Sports | 0.040000 | 0.010000 | 0.000000 | 0.000000 | 82 | 7.900000 | E |
name | platform | year | genre | na | eu | jp | other | critic_score | user_score | rating | |
---|---|---|---|---|---|---|---|---|---|---|---|
397 | LEGO Batman: The Videogame | X360 | 2008 | Action | 2.040000 | 1.020000 | 0.000000 | 0.320000 | 76 | 7.900000 | E10+ |
456 | LEGO Batman: The Videogame | Wii | Action | 1.800000 | 0.970000 | 0.000000 | 0.290000 | 74 | 7.900000 | E10+ | |
460 | LEGO Batman: The Videogame | DS | 2008 | Action | 1.750000 | 1.010000 | 0.000000 | 0.290000 | 72 | 8.000000 | E10+ |
1519 | LEGO Batman: The Videogame | PS3 | 2008 | Action | 0.720000 | 0.390000 | 0.000000 | 0.190000 | 75 | 7.700000 | E10+ |
1538 | LEGO Batman: The Videogame | PSP | Action | 0.570000 | 0.440000 | 0.000000 | 0.270000 | 73 | 7.400000 | E10+ | |
1553 | LEGO Batman: The Videogame | PS2 | 2008 | Action | 0.720000 | 0.030000 | 0.000000 | 0.520000 | 77 | 8.900000 | E10+ |
12465 | LEGO Batman: The Videogame | PC | 2008 | Action | 0.020000 | 0.030000 | 0.000000 | 0.010000 | 80 | 7.800000 | E10+ |
def fill_year (data_game, year, name):
for index in data_game[name].unique():
data_game.loc[(data_game[year].isna())&(data_game[name] == index), year] = \
data_game.loc[data_game[name] == index, year].max()
return data_game
fill_year(data_game, 'year', 'name')
print(f'Пропущенных значений в колонке year - {data_game.year.isna().sum()}, что составляет - \
{round(data_game.year.isna().mean()*100, 2)}%')
Пропущенных значений в колонке year - 146, что составляет - 0.87%
display(data_game.query('year.isnull()')['name'].reset_index().head(2), data_game.query('name == "Rock Band"'))
index | name | |
---|---|---|
0 | 475 | wwe Smackdown vs. Raw 2006 |
1 | 627 | Rock Band |
name | platform | year | genre | na | eu | jp | other | critic_score | user_score | rating | |
---|---|---|---|---|---|---|---|---|---|---|---|
627 | Rock Band | X360 | <NA> | Misc | 1.93 | 0.33 | 0.0 | 0.21 | 92 | 8.2 | T |
805 | Rock Band | Wii | <NA> | Misc | 1.33 | 0.56 | 0.0 | 0.20 | 80 | 6.3 | T |
1142 | Rock Band | PS3 | <NA> | Misc | 0.99 | 0.41 | 0.0 | 0.22 | 92 | 8.4 | T |
1840 | Rock Band | PS2 | <NA> | Misc | 0.71 | 0.06 | 0.0 | 0.35 | 82 | 6.8 | T |
# Предлагаю удалить все строки с пропусками в name, year и genre.
data_game.dropna(subset=['name', 'year', 'genre'], inplace=True)
Комментарий:
Поскольку количество пропусков в колонках critic_score
и user_score
очень большое и причины пропусков могут быть обусловлены разными факторами, предлагаю восполнить их на технические индикаторы для возможности выделения при анализе: оценки критиков и пользователей - на 0, т.к. столбцы имеют колличественные данные.
data_game.critic_score = data_game.critic_score.fillna(0)
data_game.user_score = data_game.user_score.fillna(0)
# Просмотрим уникальные значения колонки [rating]
data_game.rating.unique()
array(['E', nan, 'M', 'T', 'E10+', 'K-A', 'AO', 'EC', 'RP'], dtype=object)
Комментарий:
Столбец rating
содержит буквенное обозначение рейтинга, присвоенного видеоигре организацией ESRB (всего 8 уникальных значений + пропуски).
Справочная информация (источник - https://ru.wikipedia.org/wiki/Entertainment_Software_Rating_Board): Entertainment Software Rating Board (ESRB) — негосударственная организация, основное направление деятельности — принятие и определение рейтингов для компьютерных видеоигр и другого развлекательного программного обеспечения в США и Канаде.
Расшифровка буквенных обозначений рейтинга:
«EC» («Early childhood») — «Для детей младшего возраста»
«E» («Everyone») — «Для всех» (до 1998 года - "K-A" ("Kids to Adults"))
«E10+»(«Everyone 10 and older») — «Для всех от 10 лет и старше»
«T» («Teen») — «Подросткам»
«M»(«Mature») — «Для взрослых»
«AO» («Adults Only 18+») — «Только для взрослых»
«RP» («Rating Pending») — «Рейтинг ожидается»
В других странах существуют свои системы стандартизации и лицензирования видеоигр (например, как PEGI в Европейском Союзе, CERA в Японии). Отсутствие рейтинга в датасете может обозначать, что игра была произведена за переделами Северной Америки и не подлежала оценке ESRB. Предлагаю восполнить пропущенные значения на «Indefinite» — «Неопределенно»
data_game.rating = data_game.rating.fillna('Indefinite')
# Запишим в отдельную колонку сумму всех продаж по всем регионам
data_game['total_sales'] = data_game[['na','eu','jp','other']].sum(axis = 1)
Промежуточный вывод:
Все выявленные замечания были устранены: избавились от пропусков, изменили тип данных, убедились в отсутствии дубликатов, привели названия столбцов к удобному виду.
Посмотрите, сколько игр выпускалось в разные годы. Важны ли данные за все периоды?
def create_any_bar(groupby_column, func, y='name'):
plt.style.use('seaborn-ticks')
game_plot = data_game.groupby(groupby_column)[y]
if func == 'count':
game_plot_calculated = game_plot.count()
figsize = (15,5)
plt.ylabel('Количество в млн. шт.')
plot = game_plot_calculated.plot(kind='bar', y=y, figsize=figsize, ec='black', title = 'Количество продаж по годам')
elif func == 'sum':
game_plot_calculated = game_plot.sum().sort_values()
figsize = (15,10)
plt.xlabel('Количество в млн. шт.')
plot = game_plot_calculated.plot(kind='barh', y=y, figsize=figsize, ec='black', title = 'Сумма продаж по платформам')
create_any_bar('year', 'count')
# Агрегируем данные и посмотрим на динамику продаж в макрорегиионах
df = data_game.groupby('year', as_index=False).agg({'na':'sum', 'eu':'sum', 'jp':'sum', 'other':'sum'})
fig = px.line(df, x='year', y=['na', 'eu', 'jp', 'other'], template='plotly_white', title='Динамика продаж по макрорегионам')
fig.show()
Комментарий:
Видно, что до 1990 года количество игр было малым. 1991г. - негласно можно считать точкой отсчета в развитии и распространеннии видеоигр - это может быть связано с бурным технологическим развитичем. Резкий скачок по количеству игр, выпущенных в год, произошел в 2002 году. Пик - 2008-2009 года. После этого начался спад, связанный с развитием мобильных устройств, интеграция соц. сетей в жизнь людей. Это всё привело к тому, что интерес к самим играм упал, соответственно, упало и их производство. Также из графика Динамика продаж по макрорегионам - становится ясно, какой макрорегион создает тренды.
Интересная статья - https://www.gamesindustry.biz/gamesindustry-biz-presents-the-year-in-numbers-2019
Посмотрите, как менялись продажи по платформам. Выберите платформы с наибольшими суммарными продажами и постройте распределение по годам. За какой характерный срок появляются новые и исчезают старые платформы?
create_any_bar('platform', 'sum', 'total_sales')
Комментарий:
Лично я знакома далеко не с всеми платформами в списке, но конечно же, было очевидным, что лидером продаж является платформа PS2 - она имела популярность и была как минимум у каждого на слуху. Далее идут Xbox360, PS3 и Wii ....
Возьмите данные за соответствующий актуальный период. Актуальный период определите самостоятельно в результате исследования предыдущих вопросов. Основной фактор — эти данные помогут построить прогноз на 2017 год.
# Произведем расчет общих продаж: построим сводную таблицу по платформам
data_platforms = data_game.pivot_table(index=['platform'],
values= 'total_sales',
aggfunc='sum') \
.sort_values(by='total_sales', ascending =False).reset_index()
display(data_platforms.head(15).style.background_gradient(sns.light_palette("brown", as_cmap=True)))
platform | total_sales | |
---|---|---|
0 | PS2 | 1247.160000 |
1 | X360 | 966.610000 |
2 | PS3 | 935.190000 |
3 | Wii | 903.310000 |
4 | DS | 804.280000 |
5 | PS | 727.580000 |
6 | PS4 | 314.140000 |
7 | GBA | 313.730000 |
8 | PSP | 293.570000 |
9 | PC | 258.860000 |
10 | 3DS | 258.530000 |
11 | XB | 256.690000 |
12 | GB | 254.430000 |
13 | NES | 251.050000 |
14 | N64 | 218.480000 |
''' Проанализируем данные начала 21-го века 5-лидирующих платформ: с периода 2000 по 2016 г.г.
Именно с этого момента начинается уверенный рост рынка видеоигр '''
data_2000 = data_game[data_game['year'] >= 2000]
''' Посмотрим, как менялись продажи по платформам. Выберем платформы с наибольшими суммарными продажами и
построим распределение по годам. Ответим на вопрос: за какой характерный срок появляются новые и исчезают
старые платформы? '''
# Возьмем индексы и преобразуем их в лист
platforms = data_2000.groupby('platform')['total_sales'].sum().sort_values(ascending=False)[:5].index.tolist()
data = []
for index in platforms:
data.append(go.Bar(x=data_2000[data_2000.platform == index].groupby("year")['total_sales'].sum().index,
y=data_2000[data_2000.platform == index].groupby("year")['total_sales'].sum(), name=index))
layout = {'title': 'Продажи по годам'}
fig = go.Figure(data=data, layout=layout)
fig.layout.template = 'plotly_white'
fig.update_layout(legend_title_text = "Платформа")
fig.update_xaxes(title_text="Год")
fig.update_yaxes(title_text="Уровень продаж")
fig.show()
Промежуточный вывод:
Определенно можно заявить, что практически все игровые платформы в среднем существуют до 10 лет включительно. Примерный пик "Популярноси | Продаваемости" ≈ наступает через 5 лет после релиза. Самые успешные из них это Sony Play Station и XBOX, у Nintento WII был быстрый скачок в 2009 году и такой же резкий провал
Таким образом, в среднем консоль живет 10 лет. Это неизбежно, т.к. прогресс идет вперед и "железо" в старых консолях перестает отвечать техническим требованиям новых игр.
Нам необходимо выбрать актуальный период - период, за который мы будем оценивать наши данные, на основании выводов по которым будем строить предсказательные модели на 2017 год. Многие новые платформы, на тот период, появляются в 2006 году (например: Wii, PS3, X360), при этом характерный срок жизни - это 10 лет, но к 2013 году мы видим их активный спад, к тому видим рост актуальных на нынешнее время платформ, их в дальнейшем мы и будем разбирать. Для целей прогнозирования продаж на следующий год в динамично меняющейся индустрии, как компьютерные игры, предлагаю охватит данные с 2013 года.
data_2013 = data_game[data_game['year'] >= 2013]
Какие платформы лидируют по продажам, растут или падают? Выберите несколько потенциально прибыльных платформ.
data = []
for index in data_2013.platform.unique():
data.append(go.Bar(x=data_2013[data_2013.platform == index].groupby("year")['total_sales'].sum().index,
y=data_2013[data_2013.platform == index].groupby("year")['total_sales'].sum(), name=index))
layout = {'title': 'Продажи по годам'}
fig = go.Figure(data=data, layout=layout)
fig.layout.template = 'plotly_white'
fig.update_layout(legend_title_text = "Платформа")
fig.update_xaxes(title_text="Год")
fig.update_yaxes(title_text="Уровень продаж")
fig.show()
Комментарий:
Видим общую для всех платформ тенденцию снижения продаж, явными лидерами к 2016г. являются платформы PS4, XOne и 3DS
Постройте график «ящик с усами» по глобальным продажам игр в разбивке по платформам. Опишите результат.
#Выделим шесть топовых платформ с которыми мы будем дальше работать
data_box = data_2013.query('platform in ("PS4", "XOne", "PC", "WiiU", "3DS", "PSV")') \
.pivot_table(index=['name', 'platform', 'year'],
values='total_sales').sort_values(by='platform').reset_index()
data_box.head(10)
name | platform | year | total_sales | |
---|---|---|---|---|
0 | Beyblade Burst | 3DS | 2016 | 0.03 |
1 | Rodea the Sky Soldier | 3DS | 2015 | 0.03 |
2 | Romance of the Three Kingdoms (3DS) | 3DS | 2013 | 0.03 |
3 | Romance of the Three Kingdoms II | 3DS | 2015 | 0.03 |
4 | Dream Girl Premier | 3DS | 2015 | 0.03 |
5 | Dragon Quest X | 3DS | 2014 | 0.17 |
6 | Dragon Quest VIII: Journey of the Cursed King | 3DS | 2015 | 0.86 |
7 | Dragon Quest VII: Warriors of Eden | 3DS | 2013 | 1.46 |
8 | Dragon Quest Monsters Joker 3 | 3DS | 2016 | 0.63 |
9 | Dragon Quest Monsters 2 | 3DS | 2014 | 0.79 |
data = []
for index in data_box.platform.unique():
data.append(go.Box(y=data_box[data_box.platform == index]['total_sales'], name=index ))
layout = {'title': 'Продажи по платформам'}
fig = go.Figure(data=data, layout=layout)
fig.update_layout(yaxis=dict(range=[0,1])) # Изменим масштаб по оси y
fig.layout.template = 'plotly_white'
# Тут можем поиграться боксплотом
iplot(fig, show_link = True)
Промежуточный вывод:
Что сразу же бросается в глаза - это разное максимальное и медианное значение продаж у платформ. У явного лидера - PS4 - распределение смещено в сторону максимальных значений и это самая популярная, и успешная платформа: игры, созданные для нее, продавались большими тиражами.
Посмотрите, как влияют на продажи внутри одной популярной платформы отзывы пользователей и критиков. Постройте диаграмму рассеяния и посчитайте корреляцию между отзывами и продажами. Сформулируйте выводы.
# Выбираю PS4 - это самая популярная, и успешная платформа
sns.pairplot(data_game[data_game.platform == "PS4"][['total_sales', 'critic_score', 'user_score']])
plt.show()
/home/jupyter-zurab/.local/lib/python3.9/site-packages/seaborn/distributions.py:499: FutureWarning: In a future version, the Index constructor will not infer numeric dtypes when passed object-dtype sequences (matching Series behavior) /home/jupyter-zurab/.local/lib/python3.9/site-packages/seaborn/distributions.py:500: FutureWarning: In a future version, the Index constructor will not infer numeric dtypes when passed object-dtype sequences (matching Series behavior)
Комментарий:
Видим нулевые выбросы - это пропущенные значения, где мы условились восполнить их нулями.
# Применим корреляцию Спирмена, чтобы отбросить нулевые выбросы
data_game[data_game.platform == "PS4"][['total_sales', 'critic_score', 'user_score']].corr(method='spearman')
total_sales | critic_score | user_score | |
---|---|---|---|
total_sales | 1.000000 | 0.414235 | 0.175916 |
critic_score | 0.414235 | 1.000000 | 0.797199 |
user_score | 0.175916 | 0.797199 | 1.000000 |
Промежуточный вывод:
В целом видим слабую прямую взаимосвязь, или иными словами, продажи не зависят от оценок критиков и пользователей, но к критикам прислушиваются больше.
Соотнесите выводы с продажами игр на других платформах.
other_platforms = ['PS4','XOne','PC','WiiU','3DS', 'PSV']
rows = 3
cols = 2
fig, axes = plt.subplots(rows, cols, figsize=(15,10))
# Устанавливаем счетчик, для перебора платформ в списке через индекс
count = 0
for a in range(rows):
for b in range(cols):
# перебираем по индексу
index = other_platforms[count]
df = data_game[(data_game['platform'] == index) & (data_game['user_score'] > 0)]
df_filtered = df[['total_sales' ,'critic_score', 'user_score']]
sns.set(font_scale=1.0)
ax = sns.heatmap(df_filtered.corr()[['total_sales']].sort_values(by='total_sales', ascending=False),
cmap="Blues", annot=True, annot_kws={'size':15}, ax=axes[a,b])
ax.set_title(index, fontsize=20)
ax.set_yticklabels(ax.get_yticklabels(), rotation=0)
plt.tight_layout(pad=5)
count += 1
Промежуточный вывод:
У всех 6 платформ наблюдается не сильная связь между оценками критиков и продажами. Cвязь между оценками пользователей и продажами слабая у всех платформ, либо её совсем нет. У WiiU корреляция между отзывами пользователей и продажами - уверенная и даже выше, чем корреляция между отзывами критиков и игровой платформой.
Посмотрите на общее распределение игр по жанрам. Что можно сказать о самых прибыльных жанрах? Выделяются ли жанры с высокими и низкими продажами?
ax = plt.gca()
ratio_genres = data_2013.groupby('genre') \
.agg({'name': 'count', 'total_sales': 'sum'}) \
.sort_values(by='name', ascending=False)
games = ratio_genres['name']
games.plot(kind='bar', figsize=(15,5), ec='black', ax=ax, width=0.2, position=1)
sales = ratio_genres['total_sales']
sales.plot(kind='bar', figsize=(15,5), ec='black', ax=ax, width=0.2, color='#97F0AA', position=0)
ax.legend(['Количество продаж', 'Общая сумма продаж'])
ratio_genres['ratio'] = ratio_genres['total_sales'] / ratio_genres['name']
ratio_genres.sort_values(by='ratio', ascending=False).reset_index() \
.style.background_gradient(sns.light_palette("brown", as_cmap=True))
genre | name | total_sales | ratio | |
---|---|---|---|---|
0 | Shooter | 187 | 232.980000 | 1.245882 |
1 | Sports | 214 | 150.650000 | 0.703972 |
2 | Platform | 74 | 42.630000 | 0.576081 |
3 | Role-Playing | 292 | 145.890000 | 0.499623 |
4 | Racing | 85 | 39.890000 | 0.469294 |
5 | Fighting | 80 | 35.310000 | 0.441375 |
6 | Action | 769 | 322.500000 | 0.419376 |
7 | Misc | 156 | 63.060000 | 0.404231 |
8 | Simulation | 62 | 21.760000 | 0.350968 |
9 | Puzzle | 17 | 3.170000 | 0.186471 |
10 | Strategy | 56 | 10.080000 | 0.180000 |
11 | Adventure | 245 | 23.640000 | 0.096490 |
# Построим боксплот
data_box = data_2013.query('platform in ("PS4", "XOne", "PC", "WiiU", "3DS", "PSV")') \
.pivot_table(index=['name', 'platform', 'genre'],
values='total_sales').sort_values(by='platform').reset_index()
data = []
for index in data_box.genre.unique():
data.append(go.Box(y=data_box[data_box.genre == index]['total_sales'], name=index ))
layout = {'title': 'Продажи по жанрам'}
fig = go.Figure(data=data, layout=layout)
fig.update_layout(yaxis=dict(range=[0,2])) # Изменим масштаб по оси y
fig.layout.template = 'plotly_white'
iplot(fig, show_link = False)
Промежуточный вывод:
Самый выпускаемый жанр - Action - это связано с широким спектром данного жанра, в том смысле, что жанр представлен во множестве разновидностей от файтингов, шутеров и платформеров, сам по осебе жанр много в себя вбирает
Самый прибыльный жанр - Shooter - жанр никогда себя не изживет и всегда актуален
Жанр Sports стоит на третьем месте по продажам - (Серии FIFA, PES и пр.)
Неожиданно, что Adventure сильно отстаёт по продажам, он схож по уровню с жанром Simulation хотя игр выпускается много - (Довольно нишевый жанр, как правилило совмещенный с другими жанрами, а иначе очень быстро надоедает)
Итоги:
Вывод по третьему блоку:
При исследовании данных мы рассмотрели продажи игр взависиомти по годам, определили среднюю продолжнительность жизни платформы, определили 6 потенциально прибыльные платформы, рассмотрели их корреляцию с рейтингом пользователей и экспертов. Рассмотрели продажи игр в зависимости от жанра. Обобщив результаты можем сказать, что потенциально прибыльные игры должны быть на платформах - XOne, PS4, 3DS, с жанром Shooter, Sports и Platform с любыми рейтингами пользователей или экспертов. Мы определили общий портрет игры с наибольшими продажами. Теперь определим портрет пользователя для каждого региона.
Самые популярные платформы (топ-5). Опишите различия в долях продаж
# Группировка данных ТОП5 платформ по продажам в разрезе рынков
na = data_game[data_game.year > 2012].groupby('platform')['na'].agg(na='sum').nlargest(5, 'na').reset_index()
eu = data_game[data_game.year > 2012].groupby('platform')['eu'].agg(eu='sum').nlargest(5, 'eu').reset_index()
jp = data_game[data_game.year > 2012].groupby('platform')['jp'].agg(jp='sum').nlargest(5, 'jp').reset_index()
# График продаж по платформам в разрезе рынков
fig, ax = plt.subplots(1, 3, figsize=(15, 5))
fig.suptitle('Продажи по платформам в разрезе рынков')
sns.barplot(y='na', x='platform', data=na, ax=ax[0], palette='pastel')
ax[0].set_title('Северная Америка')
ax[0].set_ylabel('продажи, млн.шт.')
ax[0].set_xlabel('платформа')
sns.barplot(y='eu', x='platform', data=eu, ax=ax[1], palette='pastel')
ax[1].set_title('Европа')
ax[1].set_ylabel('продажи, млн.шт.')
ax[1].set_xlabel('платформа')
sns.barplot(y='jp', x='platform', data=jp, ax=ax[2], palette='pastel')
ax[2].set_title('Япония')
ax[2].set_ylabel('продажи, млн.шт.')
ax[2].set_xlabel('платформа')
plt.show()
Комментарий:
В Северной Америке и в Европе лидер - PS4, а в Японии - мобильный 3DS, Япония - последний оплот компании Nintendo, 3DS и WiiU в сумме занимают 76% местного рынка. XOne от Microsoft второй в Северной Америке, в Японии его доля - статистическая погрешность. В Европе кроме лидирущего с большим отрывом PS4 остальные платформы показывают почти одинаковые цифры.
Самые популярные жанры (топ-5). Поясните разницу.
# Группировка данных ТОП5 жанров по продажам в разрезе рынков
na_genres = data_game[data_game.year > 2012].groupby('genre')['na'].agg(na='sum').nlargest(5, 'na').reset_index()
eu_genres = data_game[data_game.year > 2012].groupby('genre')['eu'].agg(eu='sum').nlargest(5, 'eu').reset_index()
jp_genres = data_game[data_game.year > 2012].groupby('genre')['jp'].agg(jp='sum').nlargest(5, 'jp').reset_index()
# График продаж по жанрам в разрезе рынков
fig, ax = plt.subplots(1, 3, figsize=(20, 5))
fig.suptitle('Продажи по жанрам в разрезе рынков')
sns.barplot(y='na', x='genre', data=na_genres, ax=ax[0], palette='pastel')
ax[0].set_title('Северная Америка')
ax[0].set_ylabel('продажи, млн.шт.')
ax[0].set_xlabel('жанр')
sns.barplot(y='eu', x='genre', data=eu_genres, ax=ax[1], palette='pastel')
ax[1].set_title('Европа')
ax[1].set_ylabel('продажи, млн.шт.')
ax[1].set_xlabel('жанр')
sns.barplot(y='jp', x='genre', data=jp_genres, ax=ax[2], palette='pastel')
ax[2].set_title('Япония')
ax[2].set_ylabel('продажи, млн.шт.')
ax[2].set_xlabel('жанр')
plt.show()
Комментарий:
В Северной Америке и в Европе больше всего продаются игры жанра Action, затем Shooter, потом Sports
Разница между Западом и Востоком очевидна, в Японии другой лидер - жанр Role-Playing, Action идут с большим отрывом вторыми, а вот Shooter - не так популярен
Влияние рейтинга ESRB на продажи по регионам
na_rating = data_game[data_game.year > 2013].groupby('rating')['na'].agg(na_sales='sum').reset_index()
eu_rating = data_game[data_game.year > 2013].groupby('rating')['eu'].agg(eu_sales='sum').reset_index()
jp_rating = data_game[data_game.year > 2013].groupby('rating')['jp'].agg(jp_sales='sum').reset_index()
fig, ax = plt.subplots(1,3, figsize=(20, 6))
fig.suptitle('Продажи по рейтингам ESRB в разрезе рынков')
data1, categories1 = na_rating['na_sales'], na_rating['rating']
data2, categories2 = eu_rating['eu_sales'], eu_rating['rating']
data3, categories3 = jp_rating['jp_sales'], jp_rating['rating']
colors = ['#006699','#009933','#FF9900','#996600', '#CC0000']
def func(pct, allvals):
absolute = int(pct/100.*np.sum(allvals))
return "{:.1f}% ({:d} млн )".format(pct, absolute)
wedges, texts, autotexts = ax[0].pie(data1 ,autopct=lambda pct: func(pct, data1), textprops=dict(color="w"), colors=colors)
ax[0].set_title("Северная Америка")
ax[0].legend(wedges, categories1, title="рейтинг", loc="upper right")
plt.setp(autotexts, size=8, weight=850)
wedges, texts, autotexts = ax[1].pie(data2 ,autopct=lambda pct: func(pct, data2), textprops=dict(color="w"),colors=colors)
ax[1].set_title("Европа")
ax[1].legend(wedges, categories2, title="рейтинг", loc="upper right")
plt.setp(autotexts, size=8, weight=850)
wedges, texts, autotexts = ax[2].pie(data3 ,autopct=lambda pct: func(pct, data3), startangle = 65, textprops=dict(color="w"),colors=colors)
ax[2].set_title("Япония")
ax[2].legend(wedges, categories3, title="рейтинг", loc="upper right")
plt.setp(autotexts, size=8, weight=850)
plt.show()
Расшифровка буквенных обозначений рейтинга:
«EC» («Early childhood») — «Для детей младшего возраста»
«E» («Everyone») — «Для всех» (до 1998 года - "K-A" ("Kids to Adults"))
«E10+»(«Everyone 10 and older») — «Для всех от 10 лет и старше»
«T» («Teen») — «Подросткам»
«M»(«Mature») — «Для взрослых»
«AO» («Adults Only 18+») — «Только для взрослых»
«RP» («Rating Pending») — «Рейтинг ожидается»
«Indefinite» — «Неопределенно»
Промежуточный вывод:
Распределение рейтинга в Северной Америке и в Европе очень схожа - треть игр имеют рейтинг «M», пятая часть - рейтинг «Е», четверть - «Е10+» рейтинги, у примерно четверти игр рейтинг отсутствует.
В Японии с учетом восточной психо-национальной особенности, лидером в рейтинге является T — «Подросткам» - 15.9%, рейтинг «Е» - 9.6% и совсем малую часть занимают рейтинги «M» и «Е10+» В Японии рейтинг отсутствует более чем у половины игр, большое количество отсутствия рейтингов в Японии возможно объяснить наличием национальной рейтинговой организации, что справедливо и для Европы, где есть как общие так и национальные организации. Онлайн магазины тип Stream вообще могут не получать рейтинги, или пользоватся международной IARC для полностью цифровых продаж.
Вывод по четвертому блоку: «Портрет пользователя определенного рынка»
Пользователь из Северной Америки любит и покупает Action в пяти случаях из ста, чуть меньше - Shooter. В основном у него игры с рейтингом M и E. Практически у каждого второго приставка Play Station 4, у каждого третьего XOne от Microsoft.
Пользователь из Европы однозначно почти в половине случаев выбирает игры для Play Station 4, XOne у него окажется с такой же вероятностью что и игровой ноутбук или мобильная 3DS - в 13-16 случаях из ста. В остальном его предпочтения такие же как у пользователя из Северной Америки - больше всего любит Action, затем Shooter, в половине случаев с рейтингом M и E
Пользователи из Японии уникальны, как и их страна. Они не замечают и не притрагиваются к XOne от Microsoft, Свежий PS4 У японца можно встретиь в десяти случаях из ста. В отличии от старого доброго 3DS от Nintendo, игры для которой забирают 66% продаж в индустрии в последние годы. Японец не любит "стрелялки-шутеры", они его почти не интересуют, зато ролевые игры у него на первом месте, единственное что роднит его в предпочтениях с американцем и европейцем - популяность жанра Action.
Гипотеза № 1: Средние пользовательские рейтинги платформ Xbox One и PC одинаковые
Применим t-критерий Стьюдента для подтверждения гипотезы о равенстве средних двух генеральных совокупностей Определим пороговое значение p-value = 0.05
Н₀ (нулевая гипотеза) - средние пользовательские рейтинги платформ Xbox One и PC одинаковые.
Н₁(альтернативная гипотеза) - средние пользовательские рейтинги платформ Xbox One и PC отличаются между собой.
sample_xone = data_game[data_game.year > 2013].query('platform == "XOne" & user_score != 0')['user_score'] # Оценки пользователей Xbox One
sample_pc = data_game[data_game.year > 2013].query('platform == "PC" & user_score != 0')['user_score']
print('Размер выборки Xbox One:',len(sample_xone))
print('Размер выборки PC:',len(sample_pc))
Размер выборки Xbox One: 165 Размер выборки PC: 123
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
fig.suptitle('Распределение значений оценки пользователей')
sns.histplot(sample_xone, ax=axes[0,0], kde=False, bins=25)
axes[0,0].set_title('XOne')
axes[0,0].set_xlabel('Оценка')
axes[0,0].set_ylabel('Частота')
sns.histplot(sample_pc, ax=axes[0,1], kde=False, bins=25)
axes[0,1].set_title('PC')
axes[0,1].set_xlabel('Оценка')
axes[0,1].set_ylabel('Частота')
# “s” - стандартизированная строка, ожидаемая статистика заказов масштабируется на стандартное
# отклонение данной выборки и к ним добавляется среднее значение
qqplot(sample_xone, line='s', ax=axes[1,0])
qqplot(sample_pc, line='s', ax=axes[1,1])
plt.show()
alpha = 0.05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвергнем гипотезу
results_1 = st.ttest_ind(
sample_xone,
sample_pc,
equal_var = False)
print('p-значение:', results_1.pvalue)
if (results_1.pvalue < alpha):
print("Отвергаем нулевую гипотезу")
else:
print("Не получилось отвергнуть нулевую гипотезу")
p-значение: 0.139237120435483 Не получилось отвергнуть нулевую гипотезу
Вывод по гипотеза № 1:
На имеющихся данных, на уровне значимости 5% (уровне доверия 95%) нет оснований отвергнуть нулевую гипотезу о равенстве средних значений оценок пользователей платформ XOne и PC в пользу альтернативной.
Насколько я поняла XOne и PC имеют почти одинаковый набор игр, как и сам сервис [Microsoft], который работает и на PC, и на XOne, следовательно рейтинг у них будет одинаковый.
Гипотеза № 2: Средние пользовательские рейтинги жанров Action и Sports разные
Применим t-критерий Стьюдента для подтверждения гипотезы о равенстве средних двух генеральных совокупностей
Определим пороговое значение p-value = 0.05
Н₀ (нулевая гипотеза) - средние пользовательские рейтинги платформ Action и Sports одинаковые.
Н₁(альтернативная гипотеза) - средние пользовательские рейтинги платформ Action и Sports отличаются между собой.
sample_action = data_game[data_game.year > 2013].query('genre == "Action" & user_score != 0')['user_score']
sample_sports = data_game[data_game.year > 2013].query('genre == "Sports" & user_score != 0')['user_score']
print('Размер выборки Action:',len(sample_action))
print('Размер выборки Sports:',len(sample_sports))
Размер выборки Action: 298 Размер выборки Sports: 127
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
fig.suptitle('Распределение значений оценки пользователей')
sns.histplot(sample_action, ax=axes[0,0], kde=False, bins=25)
axes[0,0].set_title('Action')
axes[0,0].set_xlabel('Оценка')
axes[0,0].set_ylabel('Частота')
sns.histplot(sample_sports, ax=axes[0,1], kde=False, bins=25)
axes[0,1].set_title('Sports')
axes[0,1].set_xlabel('Оценка')
axes[0,1].set_ylabel('Частота')
# “s” - стандартизированная строка, ожидаемая статистика заказов масштабируется на стандартное
# отклонение данной выборки и к ним добавляется среднее значение
qqplot(sample_action, line='s', ax=axes[1,0])
qqplot(sample_sports, line='s', ax=axes[1,1])
plt.show()
alpha = 0.05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвергнем гипотезу
results_2 = st.ttest_ind(
sample_action,
sample_sports,
equal_var = False)
print('p-значение:', results_2.pvalue)
if (results_2.pvalue < alpha):
print("Отвергаем нулевую гипотезу")
else:
print("Не получилось отвергнуть нулевую гипотезу")
p-значение: 9.658176026869598e-15 Отвергаем нулевую гипотезу
Вывод по гипотеза № 2:
На имеющихся данных, на уровне значимости 5% (уровне доверия 95%) есть основания отвергнуть
нулевую гипотезу о различии между средними значениями оценок пользователей жанров Action и Sports в пользу альтернативы. Таким образом, остается верным утверждение, что средние пользовательские рейтинги жанров Action и Sports отличаются.
Шаг 1. Откройте файл с данными
Шаг 2. Подготовьте данные
Шаг 3. Проведите исследовательский анализ данных
Лучше всего покупают игры жанра Action, Shooter идёт на втором месте - и он самый выгодный, Role-Playing ожидаемо стоит на третьем месте по продажам.
Шаг 4. Составьте портрет пользователя каждого региона
Шаг 5. Проверьте гипотезы
Подробнее: https://allatambov.github.io/psms/pdf/hypo-test.pdf и/или https://habr.com/en/company/uchi_ru/blog/500918/